// twl_Shell
#define NO_STRICT
#include <windows.h>
#include "twl_shell.h"


TShell::TShell()
{

}

TShell::~TShell()
{
	if (m_process_id) {
		HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS,FALSE,m_process_id);
		TerminateProcess(ph,0);
		write("exit\r\n");
		WaitForSingleObject(ph,10000);
		CloseHandle(ph);
    }
}

#define CHECK(expr) if(!(expr)) throw(#expr)

char *
TShell::create(char *program)
{
  SECURITY_ATTRIBUTES attr;
  BOOL success;
  
  // ensure pipe handles are inherited!
  attr.nLength = sizeof(SECURITY_ATTRIBUTES);
  attr.bInheritHandle = TRUE;
  attr.lpSecurityDescriptor = NULL;
  HANDLE pid = GetCurrentProcess();

  try {
  HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
  CHECK(CreatePipe(&m_stdout_rd, &m_stdout_wr, &attr,0));
  CHECK(SetStdHandle(STD_OUTPUT_HANDLE, m_stdout_wr));
  CHECK(DuplicateHandle(pid,m_stdout_rd,pid,&m_stdout_rd_dup,
				  0, FALSE,DUPLICATE_SAME_ACCESS));
  CloseHandle(m_stdout_rd);

  HANDLE hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);
  CHECK(CreatePipe(&m_stdin_rd, &m_stdin_wr, &attr,0));
  CHECK(SetStdHandle(STD_INPUT_HANDLE,m_stdin_rd));
  CHECK(DuplicateHandle(pid,m_stdin_wr,pid,&m_stdin_wr_dup,
	              0, FALSE,DUPLICATE_SAME_ACCESS));
  CloseHandle(m_stdin_wr);

  create_child_process(program);

  CHECK(SetStdHandle(STD_INPUT_HANDLE,hSaveStdin));
  CHECK(SetStdHandle(STD_OUTPUT_HANDLE,hSaveStdout));
  }
  catch(char *msg) {
     return msg;
  }
  resume();
  return NULL;
}

int TShell::create_child_process(char *program)
{
 PROCESS_INFORMATION pi;
 STARTUPINFO si;


 ZeroMemory(&si,sizeof(STARTUPINFO));
 si.cb = sizeof(STARTUPINFO);
 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 si.wShowWindow = SW_HIDE;
 si.hStdInput = m_stdin_rd;
 si.hStdOutput = m_stdout_wr;
 si.hStdError = m_stdout_wr;

 BOOL bRet = CreateProcess(NULL, program, NULL,NULL,   // no security!
	                       TRUE, // inherited handles
						   CREATE_NEW_CONSOLE,
						   NULL,NULL,   // parent's environment and directory
						   &si, &pi);
 if (bRet) m_process_id = pi.dwProcessId; else m_process_id = 0;
 return bRet;
}


int TShell::write(char *str)
{
  unsigned long num_written;
  WriteFile(m_stdin_wr_dup,str,strlen(str),&num_written,NULL);
  return num_written;
}

void TShell::on_read(char *str)
{
	// does nothing!!
}

const int BUFFSIZE = 512;

int TShell::execute()
{
	char buff[BUFFSIZE];
	unsigned long num_read;
	for(;;) {
		if (!ReadFile(m_stdout_rd_dup,buff,BUFFSIZE,&num_read,NULL) || num_read==0) break;
		buff[num_read] = '\0';
		on_read(buff);
    }
	CloseHandle(m_stdin_rd);
	CloseHandle(m_stdout_wr);
	CloseHandle(m_stdin_rd_dup);
	CloseHandle(m_stdout_wr_dup);
	return 0;
}